ποΈGitΠ―ΡΠ°ποΈ
Node / meshtastic / Meshtastic-Android / files / specs / 20260711-153545-message-markdown-styling / tasks.md
specs/20260711-153545-message-markdown-styling/tasks.md 212bc596e37fd45e75b830e36cd825972f12cf79 (212bc596) Text, 13.93 KB
Tasks: Message Inline Markdown Styling (iOS Parity)
Input: Design documents from T383838specs/20260711-153545-message-markdown-styling/
Prerequisites: plan.md, spec.md, research.md, data-model.md, contracts/
Tests: Included β the spec requires pure-function unit tests in T383838commonTest (FR-005, FR-017, SC-005).
Delivery: Single effort; user-story phases are the build/verify order (P1βP2βP3), not separate PRs (per Clarifications).
Format: T383838[ID] [P?] [Story] Description
β’ [P]: parallelizable (different files, no dependency on an incomplete task)
β’ [Story]: US1 (render) / US2 (toolbar) / US3 (in-field styling); Setup/Foundational/Polish carry no story label
β’ All paths are repository-relative.
Path conventions
KMP T383838commonMain throughout: T383838core/ui/β¦, T383838feature/messaging/β¦, T383838core/resources/β¦; tests in each module's T383838commonTest.
Phase 1: Setup (Shared Infrastructure)
Purpose: dependency wiring so the parser is available to T383838core/ui.
β’ [X] T001 Add T383838markdown = "0.7.5" to T383838[versions] and T383838jetbrains-markdown = { module = "org.jetbrains:markdown", version.ref = "markdown" } to T383838[libraries] in T383838gradle/libs.versions.toml (pin matches the version already resolved transitively via T383838multiplatform-markdown-renderer 0.43.0 β verify no conflict with T383838./gradlew :core:ui:dependencies)
β’ [X] T002 Add T383838implementation(libs.jetbrains.markdown) to the T383838commonMain dependencies in T383838core/ui/build.gradle.kts
Checkpoint: T383838:core:ui resolves the parser dependency.
Phase 2: Foundational (Blocking Prerequisites)
Purpose: shared in-memory types used by rendering (US1) and reused by authoring (US2/US3). No DB/proto (data-model.md).
β οΈ CRITICAL: complete before any user-story phase.
β’ [X] T003 Create shared render types in T383838core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/component/InlineMarkdown.kt: T383838enum class InlineStyle { Bold, Italic, Strikethrough, Code, Link }, T383838data class StyleSpan(range, style), T383838data class LinkSpan(range, url), T383838data class InlineMarkdownResult(displayText, styleSpans, linkSpans) (put data classes after any top-level function to satisfy detekt T383838MatchingDeclarationName)
Checkpoint: types compile in T383838commonMain (T383838./gradlew :core:ui:kmpSmokeCompile).
Phase 3: User Story 1 β Render inline markdown (Priority: P1) π― MVP
Goal: bubbles render bold/italic/strikethrough/code/link with delimiters removed; autolink + mentions preserved.
Independent test: inject T383838Meet at **noon** by the ~~old~~ new *bridge* β \README\T383838 and LINK0 ; all five styles render, link tappable, no delimiters; a plain message and an T383838@mention still behave as before.
β’ [X] T004 [US1] Implement T383838parseInlineMarkdown(source: String): InlineMarkdownResult in T383838core/ui/.../component/InlineMarkdown.kt β walk the T383838org.jetbrains:markdown AST, emit inline T383838StyleSpan/T383838LinkSpan (bold/italic/code/link), flatten block nodes to literal text, preserve whitespace/newlines; resolve strikethrough via a GFM flavour or a minimal T383838~~β¦~~ pre-pass (research R2); total & deterministic, never throws (contract inline-markdown-parser.md)
β’ [X] T005 [P] [US1] Write parser tests in T383838core/ui/src/commonTest/kotlin/org/meshtastic/core/ui/component/InlineMarkdownTest.kt covering contract rows P-1β¦P-11 (bold, italic, T383838*** overlap, strikethrough, code-precedence, link, unpaired fallback, block-as-literal, newline, empty)
β’ [X] T006 [US1] Extend T383838buildAnnotatedStringWithLinks in T383838core/ui/.../component/AutoLinkText.kt to interleave markdown per contract autolinktext-render.md: order = mention substitution β T383838parseInlineMarkdown β URL/email/phone autolink over the stripped display text; apply mention/markdown-style/markdown-link/autolink spans in one display-space coordinate system with T383838usedIndices overlap resolution; keep T383838parseInlineMarkdown inside the existing T383838remember(text, linkStyles, mentionName) cache; short-circuit markdown parsing for emoji-only text so it renders unchanged (FR-006, parity with iOS T383838isEmoji guard); public T383838AutoLinkText(...) signature unchanged
β’ [X] T007 [P] [US1] Add render tests/asserts (in T383838core/ui T383838commonTest) for A-1β¦A-8: markdown link vs bare URL no-double-link, T383838**@!hex** mention offset correctness, parse-fail fallback to plain autolinked text, and emoji-only short-circuit (A-8 / FR-006)
β’ [X] T008 [US1] Confirm/guard search suppression: in T383838feature/messaging/.../component/MessageItem.kt verify the T383838searching branch still routes to T383838HighlightedText (plain + highlight, no markdown) and only the T383838AutoLinkText path styles markdown (FR-009); adjust if the branch leaks markdown
β’ [X] T009 [US1] Live-verify render via T383838/verify: inject the mixed-markdown message on a connected/replay device; confirm all five styles, tappable link, mention + bare URL intact, and desktop renders identically (shared T383838commonMain). DONE on emulator-5554 (fdroid-debug) + meshcon replay: injected T383838Meet at **noon** by the ~~old~~ new *bridge* β \README\T383838 and LINK0 β bubble rendered noon=bold, old=strikethrough, bridge=italic, README=monospace, map=blue tappable link, ALL delimiters stripped. Desktop shares the same T383838commonMain path (not separately screenshotted).
Checkpoint: incoming iOS markdown renders correctly β MVP is shippable on its own.
Phase 4: User Story 2 β Formatting toolbar (Priority: P2)
Goal: select text and tap Bold/Italic/Strikethrough/Code/Link to wrap/toggle; collapsed-cursor insert; link dialog.
Independent test: type "hello world", select "world", tap Bold β "hello world" (selection covers T383838**world**); tap Bold again β "hello world"; Link opens a URL dialog and wraps T383838